package water

import (
	"fmt"
	"strconv"
	"strings"
	"unicode/utf8"
)

const (
	ZeroWidthLeftToRightMark = '\u200E'
	ZeroWidthRightToLeftMark = '\u200F'
)

const (
	PositionNone  Position = 0
	PositionLeft  Position = 1
	PositionRight Position = 2
)

type Position int

func GetWatermark(text string, pos Position) (string, error) {
	watermark := getWatermark(text, pos)
	watermark, err := Decode(watermark)
	if err != nil {
		return "", err
	}
	return watermark, nil
}

func getWatermark(text string, pos Position) string {
	var watermark string
	if pos == PositionLeft {
		for i, ch := range text {
			if ch != ZeroWidthLeftToRightMark && ch != ZeroWidthRightToLeftMark {
				watermark = text[0:i]
				break
			}
		}
	}

	if pos == PositionRight {
		for i, ch := range text {
			if ch == ZeroWidthRightToLeftMark || ch == ZeroWidthLeftToRightMark {
				watermark = text[i:]
				break
			}
		}
	}
	return watermark
}

func SetWatermark(text string, pos Position, watermark string) string {

	if pos == PositionLeft {
		watermark = Encode(watermark)
		return watermark + text
	}

	if pos == PositionRight {
		watermark = Encode(watermark)
		return text + watermark
	}
	return text
}

func Encode(watermark string) string {
	var builder strings.Builder
	for _, ch := range watermark {
		builder.WriteString(BinaryToZeroWidthChars(CharToBinary(ch)))
	}
	return builder.String()
}

func Decode(watermark string) (string, error) {

	binary := ZeroWidthCharsToBinary(watermark)
	info, err := BinaryToChar(binary)
	if err != nil {
		return "", err
	}
	return info, nil
}

func BinaryToZeroWidthChars(binary string) string {
	zeroChar := "\u200E" // 零宽字符"\u200E"
	oneChar := "\u200F"  // 零宽字符"\u200F"

	var builder strings.Builder

	for _, bit := range binary {
		if bit == '0' {
			builder.WriteString(zeroChar)
		} else if bit == '1' {
			builder.WriteString(oneChar)
		}
	}
	return builder.String()
}

func ZeroWidthCharsToBinary(zeroWidthChars string) string {
	zeroChar := '\u200E' // 零宽字符"\u200E"
	oneChar := '\u200F'  // 零宽字符"\u200F"

	var binaryBuilder strings.Builder

	for _, ch := range zeroWidthChars {
		if ch == rune(zeroChar) {
			binaryBuilder.WriteRune('0')
		} else if ch == rune(oneChar) {
			binaryBuilder.WriteRune('1')
		} else {
			fmt.Println(ch)
		}
	}
	return binaryBuilder.String()
}

func BinaryToChar(binary string) (string, error) {
	// 去除空格
	binary = strings.ReplaceAll(binary, " ", "")
	if len(binary)%8 != 0 {
		return "", fmt.Errorf("invalid binary string length")
	}

	bytes := make([]byte, len(binary)/8)

	for i := 0; i < len(binary); i += 8 {
		bits := binary[i : i+8]

		value, err := strconv.ParseUint(bits, 2, 8)
		if err != nil {
			return "", err
		}
		bytes[i/8] = byte(value)
	}
	runes := make([]rune, 0, len(bytes))
	for len(bytes) > 0 {
		r, size := utf8.DecodeRune(bytes)
		runes = append(runes, r)
		bytes = bytes[size:]
	}
	return string(runes), nil
}

func CharToBinary(char rune) string {
	bytes := []byte(string(char))
	binary := ""

	for _, b := range bytes {
		if len(binary) > 0 {
			binary += " "
		}
		for i := 7; i >= 0; i-- {
			bit := (b >> uint(i)) & 1
			binary += strconv.Itoa(int(bit))
		}
	}

	return binary
}
